#include "GVector4.h"

#include "GMath.h"

namespace GMath
{

	const GVector4 GVector4::Zero	= GVector4(0.0f, 0.0f, 0.0f, 0.0f);
	const GVector4 GVector4::One	= GVector4(1.0f, 1.0f, 1.0f, 1.0f);
	const GVector4 GVector4::UnitX	= GVector4(1.0f, 0.0f, 0.0f, 0.0f);
	const GVector4 GVector4::UnitY	= GVector4(0.0f, 1.0f, 0.0f, 0.0f);
	const GVector4 GVector4::UnitZ	= GVector4(0.0f, 0.0f, 1.0f, 0.0f);
	const GVector4 GVector4::UnitW	= GVector4(0.0f, 0.0f, 0.0f, 1.0f);
	const GVector4 GVector4::Min	= GVector4(G_FLOAT_MIN, G_FLOAT_MIN, G_FLOAT_MIN, G_FLOAT_MIN);
	const GVector4 GVector4::Max	= GVector4(G_FLOAT_MAX, G_FLOAT_MAX, G_FLOAT_MAX, G_FLOAT_MAX);


	GVector4::GVector4()
	{
		x = 0.0f;
		y = 0.0f;
		z = 0.0f;
		w = 0.0f;
	}

	GVector4::GVector4(float Vx, float Vy, float Vz, float Vw)
	{
		x = Vx;
		y = Vy;
		z = Vz;
		w = Vw;
	}

	GVector4::operator float* ()
	{
		return Arr;
	}

	GVector4::operator const float* () const
	{
		return (const float*)Arr;
	}

	float GVector4::Lenght() const
	{
		return SqrtF(x * x + y * y + z * z + w * w);
	}

	float GVector4::LenghtSqr() const
	{
		return x * x + y * y + z * z + w * w;
	}

	float GVector4::Distance(const GVector4& Vector) const
	{
		float Dx = Vector.x - x;
		float Dy = Vector.y - y;
		float Dz = Vector.z - z;
		float Dw = Vector.w - w;
		return SqrtF(Dx * Dx + Dy * Dy + Dz * Dz + Dw * Dw);
	}

	float GVector4::DistanceSqr(const GVector4& Vector) const
	{
		float Dx = Vector.x - x;
		float Dy = Vector.y - y;
		float Dz = Vector.z - z;
		float Dw = Vector.w - w;
		return Dx * Dx + Dy * Dy + Dz * Dz + Dw * Dw;
	}

	void GVector4::Maximum(OUTPUT GVector4& Output, const GVector4& Vector1, const GVector4& Vector2)
	{
		Output.x = Vector1.x > Vector2.x ? Vector1.x : Vector2.x;
		Output.y = Vector1.y > Vector2.y ? Vector1.y : Vector2.y;
		Output.z = Vector1.z > Vector2.z ? Vector1.z : Vector2.z;
		Output.z = Vector1.w > Vector2.w ? Vector1.w : Vector2.w;
	}

	void GVector4::Minimum(OUTPUT GVector4& Output, const GVector4& Vector1, const GVector4& Vector2)
	{
		Output.x = Vector1.x < Vector2.x ? Vector1.x : Vector2.x;
		Output.y = Vector1.y < Vector2.y ? Vector1.y : Vector2.y;
		Output.z = Vector1.z < Vector2.z ? Vector1.z : Vector2.z;
		Output.z = Vector1.w < Vector2.w ? Vector1.w : Vector2.w;
	}

	void GVector4::Saturate()
	{
		Clamp(GVector4::Zero, GVector4::One);
	}

	void GVector4::Saturate(OUTPUT GVector4& Output, const GVector4& Vector)
	{
		GVector4::Clamp(Output, Vector, GVector4::Zero, GVector4::One);
	}

	void GVector4::Clamp(const GVector4& LimitLow, const GVector4& LimitUp)
	{
		if (x > LimitUp.x)
		{
			x = LimitUp.x;
		}
		else if (x < LimitLow.x)
		{
			x = LimitLow.x;
		}

		if (y > LimitUp.y)
		{
			y = LimitUp.y;
		}
		else if (y < LimitLow.y)
		{
			y = LimitLow.y;
		}

		if (z > LimitUp.z)
		{
			z = LimitUp.z;
		}
		else if (z < LimitLow.z)
		{
			z = LimitLow.z;
		}

		if (w > LimitUp.w)
		{
			w = LimitUp.w;
		}
		else if (w < LimitLow.w)
		{
			w = LimitLow.w;
		}
	}

	void GVector4::Clamp(OUTPUT GVector4& Output, const GVector4& Vector, const GVector4& LimitLow, const GVector4& LimitUp)
	{
		if (Vector.x > LimitUp.x)
		{
			Output.x = LimitUp.x;
		}
		else if (Vector.x < LimitLow.x)
		{
			Output.x = LimitLow.x;
		}

		if (Vector.y > LimitUp.y)
		{
			Output.y = LimitUp.y;
		}
		else if (Vector.y < LimitLow.y)
		{
			Output.y = LimitLow.y;
		}

		if (Vector.z > LimitUp.z)
		{
			Output.z = LimitUp.z;
		}
		else if (Vector.z < LimitLow.z)
		{
			Output.z = LimitLow.z;
		}

		if (Vector.w > LimitUp.w)
		{
			Output.w = LimitUp.w;
		}
		else if (Vector.w < LimitLow.w)
		{
			Output.w = LimitLow.w;
		}
	}

	void GVector4::Normalize()
	{
		// Check zero case
		float InverseLenght = 1.0f / Lenght();

		x *= InverseLenght;
		y *= InverseLenght;
		z *= InverseLenght;
		w *= InverseLenght;
	}

	void GVector4::Normalize(OUTPUT GVector4& Output, const GVector4& Vector)
	{
		// Check zero case
		float InverseLenght = 1.0f / Vector.Lenght();

		Output.x = Vector.x * InverseLenght;
		Output.y = Vector.y * InverseLenght;
		Output.z = Vector.z * InverseLenght;
		Output.w = Vector.w * InverseLenght;
	}

	void GVector4::Absolute()
	{
		x = AbsF(x);
		y = AbsF(y);
		z = AbsF(z);
		w = AbsF(w);
	}

	void GVector4::Absolute(OUTPUT GVector4& Output, const GVector4& Vector)
	{
		Output.x = AbsF(Vector.x);
		Output.y = AbsF(Vector.y);
		Output.z = AbsF(Vector.z);
		Output.w = AbsF(Vector.w);
	}

	void GVector4::Scale(float Value)
	{
		x *= Value;
		y *= Value;
		z *= Value;
		w *= Value;
	}

	void GVector4::Scale(OUTPUT GVector4& Output, const GVector4& Vector, float Value)
	{
		Output.x = Vector.x * Value;
		Output.y = Vector.y * Value;
		Output.z = Vector.z * Value;
		Output.w = Vector.w * Value;
	}

	void GVector4::Lerp(const GVector4& Vector, float Amount)
	{
		x += (Vector.x - x) * Amount;
		y += (Vector.y - y) * Amount;
		z += (Vector.z - z) * Amount;
		w += (Vector.w - w) * Amount;
	}

	void GVector4::Lerp(OUTPUT GVector4& Output, const GVector4& VectorA, const GVector4& VectorB, float Amount)
	{
		Output.x = VectorA.x + (VectorB.x - VectorA.x) * Amount;
		Output.y = VectorA.y + (VectorB.y - VectorA.y) * Amount;
		Output.z = VectorA.z + (VectorB.z - VectorA.z) * Amount;
		Output.w = VectorA.w + (VectorB.w - VectorA.w) * Amount;
	}

	float GVector4::Dot(const GVector4& Vector) const
	{
		return (x * Vector.x) + (y * Vector.y) + (z * Vector.z) + (w * Vector.w);
	}

	float GVector4::Dot(const GVector4& VectorA, const GVector4& VectorB)
	{
		return (VectorA.x * VectorB.x) + (VectorA.y * VectorB.y) + 
				(VectorA.z * VectorB.z) + (VectorA.w * VectorB.w);
	}

	GVector4& GVector4::operator += (const GVector4&  Vector)
	{
		x += Vector.x;
		y += Vector.y;
		z += Vector.z;
		w += Vector.w;
		return *this;
	}
	
	GVector4& GVector4::operator -= (const GVector4& Vector)
	{
		x -= Vector.x;
		y -= Vector.y;
		z -= Vector.z;
		w -= Vector.w;
		return *this;
	}
	
	GVector4& GVector4::operator *= (const float Value)
	{
		x *= Value;
		y *= Value;
		z *= Value;
		w *= Value;
		return *this;
	}

	GVector4& GVector4::operator /= (const float Value)
	{
		float Inverse = 1.0f / Value;
		x *= Value;
		y *= Value;
		z *= Value;
		w *= Value;
		return *this;
	}

	GVector4 GVector4::operator + () const
	{
		return *this;
	}

	GVector4 GVector4::operator - () const
	{
		return GVector4(-x, -y, -z, -w);
	}

	GVector4 GVector4::operator + (const GVector4& Vector) const
	{
		return GVector4(x + Vector.x, y + Vector.y, z + Vector.z, w + Vector.w);
	}

	GVector4 GVector4::operator - (const GVector4& Vector) const
	{
		return GVector4(x - Vector.x, y - Vector.y, z - Vector.z, w - Vector.w);
	}

	GVector4 GVector4::operator * (float Value) const
	{
		return GVector4(x * Value, y * Value, z * Value, w * Value);
	}

	GVector4 GVector4::operator / (float Value) const
	{
		float Inverse = 1.0f / Value;
		return GVector4(x * Inverse, y * Inverse, z * Inverse, w * Inverse);
	}

	const GVector4& GVector4::operator = (const GVector4& Vector)
	{
		x = Vector.x;
		y = Vector.y;
		z = Vector.z;
		w = Vector.w;

		return *this;
	}

	bool GVector4::operator == (const GVector4& Vector) const
	{
		return	EqualF(x, Vector.x) &&
				EqualF(y, Vector.y) &&
				EqualF(z, Vector.z) &&
				EqualF(w, Vector.w);
	}

	bool GVector4::operator != (const GVector4& Vector) const
	{
		return !operator==(Vector);
	}

	GVector4 operator * (float Value, const GVector4& Vector)
	{
		return GVector4(Value * Vector.x, Value * Vector.y, Value * Vector.z, Value * Vector.w);
	}

	GVector4 operator / (float Value, const GVector4& Vector)
	{
		return GVector4(Value / Vector.x, Value / Vector.y, Value / Vector.z, Value / Vector.w);
	}

}